/*
 * Copyright (c) 2006 Zsolt Szasz <zsolt at lorecraft dot com>
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

package org.lorecraft.rssappender;

import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;

public class RSSAppender extends AppenderSkeleton implements Handler {
	private final int DEFAULT_PORT = 1234;
	private final int DEFAULT_MAX_SIZE = 10;
	
	private int port = DEFAULT_PORT;
	private int maxSize = DEFAULT_MAX_SIZE;

	private List events = new ArrayList();

	private ServerRunner server;
	
	public void activateOptions() {
		server = new ServerRunner(port);
	}

	protected void append(LoggingEvent loggingEvent) {
		// if no event, exit
		if (loggingEvent == null) {
			return;
		}
		// push the event to the top of the stack
		synchronized (events) {
			events.add(0, loggingEvent);
			if (events.size() == maxSize) {
				events.remove(maxSize - 1);
			}
		}
	}

	public void close() {
		if (server != null) {
			server.stop();
		}
	}

	public boolean requiresLayout() {
		return true;
	}

	public void handle(Map requestHeaders, OutputStream out) throws IOException {
		synchronized (events) {
			StringBuffer buf = new StringBuffer();
			buf.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + 
					"<rss version=\"2.0\">\r\n" + 
					"  <channel>\r\n" + 
					"    <title>RSSAppender</title>\r\n" + 
					"    <link>http://" + requestHeaders.get("host") + "</link>\r\n" + 
					"    <description>RSS feed of log events</description>\r\n");
			
			for (Iterator i = events.iterator(); i.hasNext();) {
				LoggingEvent event = (LoggingEvent) i.next();			
				String rendered = (getLayout() == null ? event.getRenderedMessage() : getLayout().format(event));
				buf.append(
					"    <item>\r\n" + 
					"      <title>" + event.getMessage().toString() + "</title>\r\n" + 
					"\r\n" + 
					"      <link>http://" + requestHeaders.get("host") + "/rss</link>\r\n" + 
					"      <description>" + rendered + "</description>\r\n" + 
					"      <pubDate>" + formatRFC822(new Date(event.timeStamp)) + "</pubDate>\r\n" + 
					"      <guid isPermaLink=\"false\">" + event.timeStamp + "-" + event.hashCode() + "</guid>\r\n" + 
					"    </item>\r\n");
			}
			buf.append(
					"  </channel>\r\n" + 
					"</rss>");
			String generateRSS = buf.toString();
			printHeaders(out);
			out.write(generateRSS.getBytes("UTF-8"));
		}
	}

	private void printHeaders(OutputStream out) throws IOException {
		out.write(("HTTP/1.1 200 OK\r\n" + 
				"Date: " + formatRFC822(new Date()) + "\r\n" + 
				"Server: RSSAppender 0.1 (http://blog.lorecraft.com)\r\n" + 
				"Content-Type: application/xml; charset=UTF-8\r\n" + 
				"\r\n").getBytes());		
	}

	public void setPort(int port) {
		this.port = port;
	}

	public void setMaxSize(int maxSize) {
		this.maxSize = maxSize;
	}
	
	public  String formatRFC822(Date date) {
        SimpleDateFormat dateFormater = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'",Locale.US);
        dateFormater.setTimeZone(TimeZone.getTimeZone("GMT"));
        return dateFormater.format(date);
    }
	
	private class ServerRunner implements Runnable {
		private final int port;
		private Thread serverThread;
		private BareHttpd httpServer;
		
		public ServerRunner(int port) {
			this.port = port;
			serverThread = new Thread(this);
			serverThread.setDaemon(true);
			serverThread.start();
		}
		
		public synchronized void stop() {
			httpServer.stop();
			try {
				serverThread.join();
			} catch (InterruptedException e) {}
			serverThread = null;
		}

		public void run() {
			httpServer = new BareHttpd(port);
			httpServer.addHandler("/rss", RSSAppender.this);
			httpServer.start();
		}
		
	}

}
